﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using LINQPad.OpenAccess.Helpers;

namespace LINQPad.OpenAccess.Services.Internal
{
    /// <summary>
    /// constant <see cref="ICustomMemberProvider" /> implementation.
    /// </summary>
    internal class ConstantPropertyMemberProvider : ICustomMemberProvider
    {
        #region Private

        #region - Vars

        private readonly object _obj;
        private readonly PropertyDescriptorCollection _propertyCollection;
        private readonly PropertyDescriptor[] _propertyDescriptors;
        private readonly Func<object, PropertyDescriptor, object> _valueSelector;

        #endregion

        #region - Functions

        /// <summary>
        /// Performs the specified projection.
        /// </summary>
        /// <typeparam name="T">The type of the result.</typeparam>
        /// <param name="selector">The selector.</param>
        /// <returns>
        /// The result collection.
        /// </returns>
        private IEnumerable<T> Select<T>( Func<object, PropertyDescriptor, T> selector )
        {
            if ( this._propertyCollection != null )
                foreach ( PropertyDescriptor property in this._propertyCollection )
                    yield return selector( this._obj, property );
            if ( this._propertyDescriptors != null )
                for ( int i = 0; i < this._propertyDescriptors.Length; i++ )
                    yield return selector( this._obj, this._propertyDescriptors[i] );
        }

        #endregion

        #endregion

        #region Public

        #region - Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ConstantPropertyMemberProvider" /> class.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="propertyDescriptors">The property descriptors.</param>
        /// <param name="valueSelector">The value selector.</param>
        public ConstantPropertyMemberProvider( object obj, PropertyDescriptor[] propertyDescriptors, Func<object, PropertyDescriptor, object> valueSelector = null )
        {
            Guard.ArgumentNotNull( obj, "obj" );

            this._obj = obj;
            this._propertyDescriptors = propertyDescriptors;
            this._valueSelector = valueSelector;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ConstantPropertyMemberProvider" /> class.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="propertyCollection">The property collection.</param>
        /// <param name="valueSelector">The value selector.</param>
        public ConstantPropertyMemberProvider( object obj, PropertyDescriptorCollection propertyCollection, Func<object, PropertyDescriptor, object> valueSelector = null )
        {
            Guard.ArgumentNotNull( obj, "obj" );

            this._obj = obj;
            this._propertyCollection = propertyCollection;
            this._valueSelector = valueSelector;
        }

        #endregion

        #endregion

        #region ICustomMemberProvider Members

        /// <summary>
        /// Gets the names.
        /// </summary>
        /// <returns>The names.</returns>
        public IEnumerable<string> GetNames( )
        {
            return this.Select( ( o, p ) => p.DisplayName );
        }
        /// <summary>
        /// Gets the types.
        /// </summary>
        /// <returns>The types.</returns>
        public IEnumerable<Type> GetTypes( )
        {
            return this.Select( ( o, p ) => p.PropertyType );
        }
        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns>The values.</returns>
        public IEnumerable<object> GetValues( )
        {
            if ( this._valueSelector == null )
                return this.Select( ( o, p ) => p.GetValue( o ) );
            return this.Select( this._valueSelector );
        }

        #endregion
    }
}
